#导入该任务需要用到的包
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns;sns.set()
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import accuracy_score
import warnings
warnings.filterwarnings('ignore')


#导入数据集
df = pd.read_csv('E:\\下载\\StudentPerformance.csv')

#输出数据集前五行
print(df.head())

#输出数据集（行数，列数）
print(df.shape)

#输出缺失值（数据清洗）
print(df.isnull().sum())

#数据集的概览
print(df.info())

#各列的描述性统计信息，包括数值型和非数值型列的详细统计信息
print(df.describe(include = 'all'))

#查看类别特征
print('Relation:',df['Relation'].unique())
print('Stage:',df['StageID'].unique())
print('StudentAbsenceDays:',df['StudentAbsenceDays'].unique())
print('Class:',df['Class'].unique())

#检查数据集是否平衡
#三类成绩分布的数量没有较大偏差，所以该数据集是一个样本均衡的数据集
sns.countplot(x='Class',order = ['L','M','H'],data = df)
plt.show()

#数据可视化
#分类特征之间的可视化
#男、女数量
sns.countplot(x = 'gender',order = ['M','F'],data = df)
plt.show()

#课程分布
sns.set(rc = {'figure.figsize':(14,8)})
sns.countplot(x = 'Topic',data = df)
plt.show()

#课程和成绩的关系
sns.set(rc = {'figure.figsize':(16,10)})
sns.countplot(x = 'Topic',hue = 'Class',hue_order = ['L','M','H'],data = df)
plt.show()

#性别和成绩的关系
sns.set(rc = {'figure.figsize':(8,4)})
sns.countplot(x = 'gender',hue = 'Class',hue_order = ['L','M','H'],data = df)
plt.show()

#性别和学科的关系
sns.set(rc = {'figure.figsize':(10,6)})
sns.countplot(x = 'Topic',hue = 'gender',hue_order = ['M','F'],data = df)
plt.show()

#课程、性别计数
df_temp = df[['Topic','gender']]
df_temp['Count'] = 1
df_temp = df_temp.groupby(['Topic','gender']).agg('sum').reset_index()
df_temp.head()
print(df_temp.head())

#课程计数
df_temp2 = df_temp.groupby('Topic').agg('sum').reset_index()
df_temp2.head()
print(df_temp.head())

#课程、性别、count_x、count_y
df_temp = pd.merge(df_temp,df_temp2,on = 'Topic',how = 'left')
df_temp.head()
print(df_temp.head())

#课程、性别、count_x、count_y、课程中的性别比例
df_temp['gender proportion in topic'] = df_temp['Count_x']/df_temp['Count_y']
df_temp.head()
print(df_temp.head())

#班级、成绩相关性
sns.countplot(x = 'SectionID',hue = 'Class',hue_order = ['L','M','H'],data = df)
plt.show()

#
df.info()

#分类型特征和数字型特征之间的可视化
#学生成绩分类（Class） 和 访问资源数量（VisITedResources） 的关系
#学生成绩分类（Class） 和 查看公告的次数（AnnouncementsView） 的关系。
#学生成绩分类（Class） 和 举手次数（raisedhands） 的关系。
#学生成绩分类（Class） 和 参与讨论的次数（Discussion） 的关系。
fig,axes = plt.subplots(2,2,figsize = (14,10))

sns.barplot(x = 'Class',y = 'VisITedResources',data = df,order = ['L','M','H'],ax = axes[0,0])
sns.barplot(x = 'Class',y = 'AnnouncementsView',data = df,order = ['L','M','H'],ax = axes[0,1])
sns.barplot(x = 'Class',y = 'raisedhands',data = df,order = ['L','M','H'],ax = axes[1,0])
sns.barplot(x = 'Class',y = 'Discussion',data = df,order = ['L','M','H'],ax = axes[1,1])
plt.show()

#不同性别，举手次数对学习成绩的影响
sns.set(rc = {'figure.figsize':(8,6)})
sns.swarmplot(x = 'Class',y = 'raisedhands',hue = 'gender',order = ['L','M','H'],data = df)
plt.show()

#上课参与讨论的积极程度与成绩的关系
#参与课堂讨论的次数越多，其相应的成绩也越高
sns.set(rc = {'figure.figsize':(8,6)})
sns.boxplot(x = 'Class',y = 'Discussion',order = ['L','M','H'],data = df)
plt.show()

#数字型特征之间的可视化
#第一个子图（axes[0]）查看了 举手次数 和 查看公告次数 之间的关系。
#第二个子图（axes[1]）查看了 举手次数 和 参与讨论次数 之间的关系。
fig,axes = plt.subplots(2,1,figsize = (8,8))

sns.regplot(x = 'raisedhands',y = 'AnnouncementsView',order = 4,data = df,ax = axes[0])
sns.regplot(x = 'raisedhands',y = 'Discussion',order = 4,data = df,ax = axes[1])
plt.show()

#相关性矩阵（correlation matrix）
corr = df[['raisedhands','Discussion','VisITedResources','AnnouncementsView']].corr()
print(corr)

#相关性矩阵的可视化
sns.heatmap(corr,fmt = 'f',annot = True,xticklabels = corr.columns,yticklabels = corr.columns)
plt.show()





#模型训练
#原始数据建模
#
df.head()

#
labels = df['Class']
features = df.drop('Class',axis = 1)
features = pd.get_dummies(features)

train_features,test_features,train_labels,test_labels = (
    train_test_split(features,labels,test_size = 0.3,random_state = 0))

print('训练样本的大小:',train_features.shape)
print('训练标签的大小:',train_labels.shape)
print('测试样本的大小:',test_features.shape)
print('测试标签的大小:',test_labels.shape)

#导入逻辑回归模型
LR = LogisticRegression()
LR.fit(train_features,train_labels)
prediction = LR.predict(test_features)

score = accuracy_score(prediction,test_labels)
print(score)

#删除部分特征建模
df2 = df

labels2 = df2['Class']
features2 = df2.drop('Class',axis = 1)

#删除部分特征
features2 = features2.drop(['StageID','GradeID','SectionID'],axis = 1)

features2 = pd.get_dummies(features2)

#切分训练集个测试集
train_features2,test_features2,train_labels2,test_labels2 = train_test_split(features2,labels2,test_size = 0.3,random_state = 0)

print('训练样本的大小:',train_features2.shape)
print('训练标签的大小:',train_labels2.shape)
print('测试样本的大小:',test_features2.shape)
print('测试标签的大小:',test_labels2.shape)

#
LR2 = LogisticRegression()
LR2.fit(train_features2,train_labels2)
prediction2 = LR2.predict(test_features2)

score2 = accuracy_score(prediction2,test_labels2)
print(score2)

#增加新特征
df3 = df
labels3 = df3['Class']
df3['DiscussionPlusVisit'] = df3['Discussion'] + df3['VisITedResources']
features3 = df3.drop('Class',axis = 1)
features3 = features3.drop(['StageID','GradeID','SectionID'],axis = 1)

features3 = pd.get_dummies(features3)

#切分训练集个测试集
train_features3,test_features3,train_labels3,test_labels3 = train_test_split(features3,labels3,test_size = 0.3,random_state = 0)

print('训练样本的大小:',train_features3.shape)
print('训练标签的大小:',train_labels3.shape)
print('测试样本的大小:',test_features3.shape)
print('测试标签的大小:',test_labels3.shape)

#
LR3 = LogisticRegression()
LR3.fit(train_features3,train_labels3)
prediction3 = LR3.predict(test_features3)

score3 = accuracy_score(prediction3,test_labels3)
print(score3)

#集成算法
#
from sklearn.ensemble import RandomForestClassifier

RF = RandomForestClassifier(n_estimators = 100)
RF.fit(train_features,train_labels)
RF_prediction = RF.predict(test_features)

RF_score = accuracy_score(RF_prediction,test_labels)
print(RF_score)

#
feature_importance = pd.DataFrame({'feature':features.columns,'importance':RF.feature_importances_})
feature_importance = feature_importance.sort_values(by = 'importance',ascending = False)
print(feature_importance)

#生成图
sns.set(rc = {'figure.figsize':(20,50)})
sns.barplot(y = 'feature',x = 'importance',data = feature_importance)
plt.yticks(fontsize=6)
plt.tight_layout()
plt.show()